#include<iostream>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

//命令行长度和格式
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

//主机名最大长度
#define MAX_HOST_NANE 124

//用于命令行分析的
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc=0;
//不用环境变量获取路径
char cwd[1024];
char cwdenv[2048];//修改环境变量

//echo命令获取退出码
int lastcode=0;

//环境变量表
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs=0;



const char* GetUserName()
{
    const char* name =getenv("USER");
    return name==nullptr?"None":name;
}

const char* GetHostName()
{
    //用环境变量不稳定，可以export设置（临时），到配置文件里修改（永久）
    const char* name =getenv("HOSTNAME");
    return name==nullptr?"None":name;
    //用系统调用获取，更加稳定
    // char* name;
    // if(gethostname(name,MAX_HOST_NANE)==0) 
    // {
    //     const char* hostname = name;
    //     return hostname;
    // }
    // else return nullptr; 
}
//这里用环境变量不太好，用cwd，防止cd命令切换打印出来的路径不会变，因为环境变量不变
const char* GetPwd()
{
    //第一次修改：环境变量
    //const char* name =getenv("PWD");
    //第二次修改：用系统调用：
    // const char* name =getcwd(cwd,sizeof(cwd));
    // return name==nullptr?"None":name;
    //第三次修改：修改环境变量
    const char* name =getcwd(cwd,sizeof(cwd));
    if(name!=nullptr)
    {
        snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
        putenv(cwdenv);
    }
    return name==nullptr?"None":getenv("PWD");//环境变量被修改这里依然可以用name
}

const char* GetHome()
{
    const char* name =getenv("HOME");
    return name==nullptr?"":name;
}
//第一步：打印命令行
//1.1修改路径的显示
std::string DirName(const char* pwd)
{
#define SLASH "/"
    std::string dir=pwd;
    if(dir==SLASH) return SLASH;
    auto pos = dir.rfind(SLASH);
    if(pos == std::string::npos) return "BUG";
    return dir.substr(pos+1);
}

void MakeCommandLine(char prompt[],int size)
{
    snprintf(prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
}
void PrintCommandPrompt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt,sizeof(prompt));
    printf("%s",prompt);
    fflush(stdout);
}

//第二步：获取用户输入
bool GetCommandLine(char* commandline,int size)
{
    char* c=fgets(commandline,size,stdin);
    if(c==NULL) return false;
    commandline[strlen(commandline)-1]=0;//清除换行符
    if(strlen(commandline)==0) return false;//用户只输入了一个换行符，返回false，循环继续
    return true;
}
//第三步：解析命令行
bool CommandParse(char* commandline)
{
#define SPE " "
    g_argc=0;
    g_argv[g_argc++]=strtok(commandline,SPE);
    //while((bool)(g_argv[g_argc++]=strtok(nullptr,SPE)))
    //或者
    char* s;
    while(s)
    {
        s=strtok(nullptr,SPE);//注意最后一个是NULL
        g_argv[g_argc++]=s;
    }
    g_argc--;//这里会多一个NULL
    return true;
}
void PrintArgv()//测试我们命令行分析代码
{
    for(int i=0;i<g_argc;i++)
    {
        printf("%s ",g_argv[i]);
    }
    printf("\n");
}
//第四步：执行命令
int Execute()
{
    pid_t id=fork();
    if(id==0)
    {
        //child
        execvp(g_argv[0],g_argv);
        exit(1);
    }
    //father
    int status=0;
    pid_t rid = waitpid(id,&status,0);
    if(rid>0)
    {
        lastcode=WEXITSTATUS(status);//拿到退出码，和echo $?配合使用
    }
    return 0;
}
//检查内键命令
bool Cd()
{
    if(g_argc==1)
    {
        std::string home=GetHome();
        if(home.empty()) return true;
        chdir(home.c_str());
    }
    else
    {
        std::string where=g_argv[1];
        if(where=="-")
        {
            // const char* pwd = getenv("OLDPWD");
            // chdir(pwd);
        }
        else if(where=="~")
        {
            std::string home = GetHome();
            if(home.empty()) return true;
            chdir(home.c_str());
        }
        else
        {
            chdir(where.c_str());
        }
    }
    return true;
}
bool Echo()
{
    if(g_argc==2)
    {
        std::string opt=g_argv[1];
        if(opt=="$?")//获取退出码
        {
            std::cout<<lastcode<<std::endl;
            lastcode=0;
            return true;
        }
        if(opt[0]=='$')//第一个字符是$
        {
            std::string env_name=opt.substr(1);
            const char* env_value=getenv(env_name.c_str());
            if(env_value)
                std::cout<<env_value<<std::endl;
        }
    }
    return true;
}
bool CheckAndExecBuiltin()
{
    std::string cmd=g_argv[0];
    if(cmd=="cd")
    {
        Cd();
        return true;
    }
    else if(cmd=="echo")
    {
        Echo();
        return true;
    }
    return false;
}

//获取环境变量
void InitEnv()
{
    extern char** environ;
    memset(g_env,0,sizeof(g_env));
    g_envs=0;
    for(int i=0;environ[i];i++)
    {
        g_env[i]=(char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i],environ[i]);
        g_envs++;
    }
    g_env[g_envs]=NULL;
    for(int i=0;g_env[i];i++)
    {
        putenv(g_env[i]);
    }
    environ=g_env;
}

int main()
{
    InitEnv();
    while(1)
    {
        // 第一步：打印命令行
        PrintCommandPrompt();
        // 第二步：获取用户输入
        char commandline[COMMAND_SIZE];
        if(!GetCommandLine(commandline, sizeof(commandline)))
            continue;
        // 第三步：解析命令行
        CommandParse(commandline);
        //PrintArgv();
        if(CheckAndExecBuiltin()) 
            continue;
        //第四步：执行命令
        Execute();

    }
    return 0;
}